package com.qen.encrypt.properties;

import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.util.StrUtil;
import com.qen.encrypt.exception.EncryptException;
import com.qen.encrypt.utils.AESUtils;
import com.qen.encrypt.utils.ParamEncrypt;
import com.qen.encrypt.utils.RSAUtils;
import com.qen.encrypt.utils.SM2Helper;
import com.qen.encrypt.utils.SM4Helper;
import com.qen.encrypt.utils.SignCheck;
import lombok.Data;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 签名-加密，分组
 * @author licz
 * @data 2025年05月30日 2:15 PM
 */
@Data
public class EncryptMapConfig {

    private volatile static ConcurrentHashMap<String, EncryptMap> CONCURRENT_ENCRYPT_MAP = new ConcurrentHashMap<>();
    private final static String DEFAULT = "default";

    private final static String DEFAULT_KEY = "www.qenqenkj.com";
    private final static String DEFAULT_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1sX/kOJMwHX5KT+vfAG/7YWNST4jp0bqKXh13sFPArePhVfAqjn2eCSLgY0bA0JW5A9aAsvJeYunKtefZK1TZYbcxR1hX9PTWtivemnPKzWJ9qXCuPdo6FHBX6C+TsaNNutTK4HqxVtnI64rPnKmEojvLAZoWJAIslnU7ipWlQQIDAQAB";
    private final static String DEFAULT_PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALWxf+Q4kzAdfkpP698Ab/thY1JPiOnRuopeHXewU8Ct4+FV8CqOfZ4JIuBjRsDQlbkD1oCy8l5i6cq159krVNlhtzFHWFf09Na2K96ac8rNYn2pcK492joUcFfoL5Oxo0261MrgerFW2cjris+cqYSiO8sBmhYkAiyWdTuKlaVBAgMBAAECgYAISRV0hj+bYS5DZsTHk6jyTR+0SZ62z5gtW6XbMaixamyazRUJknENiMv08L9cUcFmIWlBz1yTQSOkBBVpd0WkRsTcOG5+Ax42VvZ/PW54Jt3DvhsCNgQJ1IlMo2AHdAKq21NsJa1EtAGsNv3CCzXNUUxOacEOEQxhYSuG/axevQJBANegUUrOSV3cu2eUhi65cWizQN05LzBErlH/9ySZeEcVm4pv/61gZfMZp6o75BhR6k3MZXvn1kkW9ddKQJK49lUCQQDXtqqJ4zKM8EdZdJJeSj/sedkfcRxbbyti6vEHJJg7lJiKspMgiUFHm5dt9BFyApG/iMxCHvdqCeSozfRvDCc9AkEAmD6wxzGp6aUNiQ/8QgdbhMJz/P8AS4Ni1bD2DhhyLTabcP6tNvxGX4Pwtyi8X1OPLMkxqm/CBC86+3yI5U/FVQJAbDGzkduB1E0EDGs6qs5lB5AKBgoupfG5ejDK2cFSFbgvVonA37XYJzl0F3MueNevELllcdikUexKYxyB+FzJJQJBANEGgt+JDZUbEzfQrwHob19SGKEwHBxWLTcze/d3MOpsvK0jwYbpFx1ROrOySVhaeMuGRoy3QPleHv573sQ47DI=";

//    private String defaultKey = DEFAULT_KEY;
//    private byte[] defaultPublicKey = DEFAULT_PUBLIC_KEY.getBytes(StandardCharsets.UTF_8);
//    private byte[] defaultPrivateKey = DEFAULT_PRIVATE_KEY.getBytes(StandardCharsets.UTF_8);

//    private String key;
//    private EncryptMap map;

    @Data
    public static class EncryptMap {
        private ParamEncrypt paramEncrypt;
        private SignCheck signCheck;
        private String key;
    }

    public EncryptMapConfig(EncryptProperties properties) {
        AlgorithmEnum encryptEnum = AlgorithmEnum.valueOf(properties.getEncryptMode());
        AlgorithmEnum signEnum = AlgorithmEnum.valueOf(properties.getSignMode());
        String defaultKey = null;
        if (StrUtil.isNotBlank(properties.getDefaultKey())) {
            defaultKey = properties.getDefaultKey();
        } else {
            defaultKey = DEFAULT_KEY;
        }
        byte[] defaultPrivateKey = null;
        byte[] defaultPublicKey = null;
        if (StrUtil.isNotBlank(properties.getDefaultPrivateKey())) {
            defaultPrivateKey = Base64.decode(properties.getDefaultPrivateKey());
        } else if (StrUtil.isNotBlank(properties.getDefaultPrivatePath())) {
            try {
                FileReader reader = new FileReader(new ClassPathResource(properties.getDefaultPrivatePath()).getFile());
                defaultPrivateKey = reader.readBytes();
            } catch (IOException e) {
                e.printStackTrace();
                throw new EncryptException(e, "加载私钥文件：" + properties.getDefaultPrivatePath() + "失败，请核对");
            }
        } else if (StrUtil.isNotBlank(properties.getDefaultAbsolutePrivatePath())) {
            FileReader reader = new FileReader(new File(properties.getDefaultAbsolutePrivatePath()));
            defaultPrivateKey = reader.readBytes();
        } else {
            defaultPrivateKey = Base64.decode(DEFAULT_PRIVATE_KEY);
        }

        if (StrUtil.isNotBlank(properties.getDefaultPublicKey())) {
            defaultPublicKey = Base64.decode(properties.getDefaultPublicKey());
        } else if (StrUtil.isNotBlank(properties.getDefaultPublicPath())) {
            try {
                FileReader reader = new FileReader(new ClassPathResource(properties.getDefaultPublicPath()).getFile());
                defaultPublicKey = reader.readBytes();
            } catch (IOException e) {
                e.printStackTrace();
                throw new EncryptException(e, "加载私钥文件：" + properties.getDefaultPublicPath() + "失败，请核对");
            }
        } else if (StrUtil.isNotBlank(properties.getDefaultAbsolutePublicPath())) {
            FileReader reader = new FileReader(new File(properties.getDefaultAbsolutePublicPath()));
            defaultPublicKey = reader.readBytes();
        } else {
            defaultPublicKey = Base64.decode(DEFAULT_PUBLIC_KEY);
        }
        ParamEncrypt paramEncrypt = initParamEncrypt(encryptEnum, defaultKey, defaultPrivateKey, defaultPublicKey);
        SignCheck check = initSignCheck(signEnum, defaultPrivateKey, defaultPublicKey);
        setEncryptMap(paramEncrypt, check, DEFAULT);

        if (CollectionUtils.isEmpty(properties.getList())) {
            return;
        }
        for (EncryptProperties.EncryptChild child : properties.getList()) {

            String key = child.getKey();

            byte[] privateKey = null;
            byte[] publicKey = null;
            if (StrUtil.isNotBlank(child.getPrivateKey())) {
                privateKey = Base64.decode(child.getPrivateKey());
            } else if (StrUtil.isNotBlank(child.getPrivatePath())) {
                try {
                    FileReader reader = new FileReader(new ClassPathResource(child.getPrivatePath()).getFile());
                    privateKey = reader.readBytes();
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new EncryptException(e, "加载私钥文件：" + child.getPrivatePath() + "失败，请核对");
                }
            } else if (StrUtil.isNotBlank(child.getAbsolutePrivatePath())) {
                FileReader reader = new FileReader(new File(child.getAbsolutePrivatePath()));
                privateKey = reader.readBytes();
            }

            if (StrUtil.isNotBlank(child.getPublicKey())) {
                publicKey = Base64.decode(child.getPublicKey());
            } else if (StrUtil.isNotBlank(child.getPublicPath())) {
                try {
                    FileReader reader = new FileReader(new ClassPathResource(child.getPublicPath()).getFile());
                    publicKey = reader.readBytes();
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new EncryptException(e, "加载公钥文件：" + child.getPublicPath() + "失败，请核对");
                }
            } else if (StrUtil.isNotBlank(child.getAbsolutePublicPath())) {
                FileReader reader = new FileReader(new File(child.getAbsolutePublicPath()));
                publicKey = reader.readBytes();
            }
//            configChild.setGroup(child.getGroup());
            String group = child.getGroup();

            AlgorithmEnum a = AlgorithmEnum.valueOf(child.getEncryptMode());
            AlgorithmEnum b = AlgorithmEnum.valueOf(child.getSignMode());
            ParamEncrypt encrypt = initParamEncrypt(a, key, privateKey, publicKey);
            SignCheck sign = initSignCheck(b, privateKey, publicKey);
            setEncryptMap(encrypt, sign, group);
        }

    }

    private ParamEncrypt initParamEncrypt(AlgorithmEnum encryptEnum, String defaultKey, byte[] defaultPrivateKey, byte[] defaultPublicKey) {
        ParamEncrypt paramEncrypt;
        switch (encryptEnum) {
            case AES_ECB:
            case AES_CBC:
                paramEncrypt = new AESUtils(defaultKey, encryptEnum);
                break;
            case SM2:
                paramEncrypt = new SM2Helper(defaultPrivateKey, defaultPublicKey);
                break;
            case RSA:
                paramEncrypt = new RSAUtils(defaultPrivateKey, defaultPublicKey);
                break;
            case SM4_CTR:
            case SM4_ECB:
                paramEncrypt = new SM4Helper(encryptEnum, defaultKey);
                break;
            default:
                StringBuilder v = new StringBuilder();
                for (AlgorithmEnum value : AlgorithmEnum.values()) {
                    v.append(value).append(";");
                }
                throw new EncryptException("构造加解密对象失败，没有对应类型，只能是：" + v);
//                break;
        }
        return paramEncrypt;
    }

    private SignCheck initSignCheck(AlgorithmEnum signEnum, byte[] defaultPrivateKey, byte[] defaultPublicKey) {
        SignCheck signCheck;
        switch (signEnum) {
            case SM2:
                signCheck = new SM2Helper(defaultPrivateKey, defaultPublicKey);
                break;
            case RSA:
                signCheck = new RSAUtils(defaultPrivateKey, defaultPublicKey);
                break;
            case SM4_CTR:
            case SM4_ECB:
            case AES_ECB:
            case AES_CBC:
            default:
                throw new EncryptException("构造签名对象失败，没有对应类型，只能是SM2和RSA");
//                break;
        }
        return signCheck;
    }

    public synchronized void setEncryptMap(ParamEncrypt paramEncrypt, SignCheck signCheck, String key) {
        EncryptMap encryptMap = new EncryptMap();
        encryptMap.setSignCheck(signCheck);
        encryptMap.setParamEncrypt(paramEncrypt);
        CONCURRENT_ENCRYPT_MAP.put(key, encryptMap);
    }

    public synchronized void setParamEncrypt(String key, ParamEncrypt paramEncrypt) {
        EncryptMap encryptMap = CONCURRENT_ENCRYPT_MAP.get(key);
        if (encryptMap == null) {
            encryptMap = new EncryptMap();
        }
        encryptMap.setParamEncrypt(paramEncrypt);
        CONCURRENT_ENCRYPT_MAP.put(key, encryptMap);
    }

    public synchronized void setSignCheck(String key, SignCheck signCheck) {
        EncryptMap encryptMap = CONCURRENT_ENCRYPT_MAP.get(key);
        if (encryptMap == null) {
            encryptMap = new EncryptMap();
        }
        encryptMap.setSignCheck(signCheck);
        CONCURRENT_ENCRYPT_MAP.put(key, encryptMap);
    }

    public void setParamEncrypt(ParamEncrypt paramEncrypt) {
        setParamEncrypt(DEFAULT, paramEncrypt);
    }

    public void setSignCheck(SignCheck signCheck) {
        setSignCheck(DEFAULT, signCheck);
    }

    public ParamEncrypt getParamEncrypt(String key) {
        EncryptMap encryptMap = CONCURRENT_ENCRYPT_MAP.get(key);
        if (encryptMap == null) {
            encryptMap = CONCURRENT_ENCRYPT_MAP.get(DEFAULT);
        }
        if (encryptMap.getParamEncrypt() == null) {
            throw new EncryptException("找不到对应的参数解密对象");
        }
        return encryptMap.getParamEncrypt();
    }

    public SignCheck getSignCheck(String key) {
        EncryptMap encryptMap = CONCURRENT_ENCRYPT_MAP.get(key);
        if (encryptMap == null) {
            encryptMap = CONCURRENT_ENCRYPT_MAP.get(DEFAULT);
        }
        if (encryptMap.getSignCheck() == null) {
            throw new EncryptException("找不到对应的签名对象");
        }
        return encryptMap.getSignCheck();
    }

    public ParamEncrypt getParamEncrypt() {
        return getParamEncrypt(DEFAULT);
    }

    public SignCheck getSignCheck() {
        return getSignCheck(DEFAULT);
    }

    public String getDefaultKey() {
        return CONCURRENT_ENCRYPT_MAP.get(DEFAULT).getKey();
    }

}
